package com.jmb.apiplatform.cores.parser;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jmb.apiplatform.common.Env;
import com.jmb.apiplatform.cores.common.NodeRequest;
import com.jmb.apiplatform.enumPro.RelyType;
import com.jmb.apiplatform.exception.BusinessException;
import com.jmb.apiplatform.exception.ParseException;
import com.jmb.apiplatform.exception.SqlException;
import com.jmb.apiplatform.pojo.*;
import com.jmb.apiplatform.service.*;
import com.jmb.apiplatform.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
public class Parser implements NodeRequest {

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ApiCaseSuiteService apiCaseSuiteService;
    @Autowired
    private ApiTestCaseRelyDataService apiTestCaseRelyDataService;
    @Autowired
    DbService dbService;

    @Autowired
    private ApiTestCaseExecuteLogService apiTestCaseExecuteLogService;
    @Autowired
    private RelyDataService relyDataService;
    @Autowired
    Env env;


    // 匹配$.{xxx}等数据
    private static final String DEPENDENCY_REGEX = "\\$\\{.+?}";
    // 匹配字母和数字数据
    private static final String DEPENDENCY_REGEX_INDEX = "[a-zA-Z]+\\[[0-9]+]";
    private static final String DEPENDENCY_REGEX_PARAMS = "\\w+\\((,?|(\'.*\')?|\\s?)+\\)$";
    // 匹配#.{xxx}等数据
    private static final String PROCESSOR_REGEX = "#\\{.+?}";
    @Autowired
    private ApiTestCaseService apiTestCaseService;


    /**
     * 字符清洗
     * @param s 待清洗数据
     * @param chainNo 调用链路跟踪 每次调用均会将自增日志编号写入缓存，再序列化
     * @param suiteId 测试套件编号，主要用于调用入口为测试套件时确定运行环境，否则应该传参null
     * @param isFailedRetry 判断该执行日志是否为失败重试，0是1否，主要用于测试报告统计断言情况
     * @param suitelogDetailNo suitelogNo仅记录重跑和真正运行的，suitelogDetailNo会包括用例所依赖的case
     * @param globalHeaders 测试套件中的全局请求headers
     * @param globalParams 测试套件中的全局请求params
     * @param globalData 测试套件中的全局请求data
     * @param casePreNo 前置用例UUID编号
     * @return 清洗后的数据
     * @throws ParseException ParseException
     * @throws BusinessException BusinessException
     * @throws SqlException SqlException
     */
    public String parseDependency(String s, String chainNo, Integer suiteId, Integer isFailedRetry, String suitelogDetailNo,
                                  HashMap globalHeaders, HashMap globalParams, HashMap globalData, String casePreNo)
            throws ParseException, BusinessException, SqlException {
        if (s == null || s.isEmpty()) {
            return s;
        }
        // 解析处理器
        s = parseProcessor(s, suitelogDetailNo, casePreNo, chainNo);

        log.info("--------------------------------------开始字符ParseUtil串解析流程--------------------------------------");
        log.info("--------------------------------------待解析字符串原文={}", s);
        Byte runEnv;
        if (suiteId == null) {
            runEnv = 4;
        } else {
            runEnv = apiCaseSuiteService.findApiCaseSuiteInfoById(suiteId).getRunDev();
        }
        log.info("--------------------------------------运行环境={}, 0dev 1test 2stg 3prod 4debug", runEnv);
        Pattern p = Pattern.compile(DEPENDENCY_REGEX);
        Matcher matcher = p.matcher(s);
        while (matcher.find()) {
            String findStr = matcher.group();
            String relyName = findStr.substring(2, findStr.length() - 1);
            String relyExpress = relyName; // 带索引的
            log.info("relyName={}", relyName);
// 进入数组下标取值模式
            if (Pattern.matches(DEPENDENCY_REGEX_INDEX, relyName)) {
                long start = TimeUtil.now();
                log.info("--------------------------------------进入数组下标取值模式");
                if (relyName.indexOf("[") != relyName.lastIndexOf("[") ||
                        relyName.indexOf("]") != relyName.lastIndexOf("]")) {
                    log.error("array index error, relyName={}", relyName);
                    throw new ParseException("array index error");
                }
                String indexStr = relyName.substring(relyName.indexOf("[") + 1, relyName.length() - 1);
                try {
                    int index = Integer.parseInt(indexStr);
                    log.info("数据下标={}", index);
                    relyName = relyName.substring(0, relyName.indexOf("["));
                    log.info("去除下标后的真实relyName={}", relyName);
                    ApiTestCaseRelyDataTO caseRelyDataTO = new ApiTestCaseRelyDataTO();
                    caseRelyDataTO.setRelyName(relyName);
                    log.info("根据relyName查询用例信息，relyName={}", relyName);
                    ApiTestCaseRelyData interfaceCaseRelyDataVO = apiTestCaseRelyDataService.findIfRelyDataByName(relyName);
                    if (null == interfaceCaseRelyDataVO) {
                        String nf = String.format("dependency ${%s} not found", relyName);
                        log.error(nf);
                        throw new ParseException(nf);
                    }
                    Integer caseId = interfaceCaseRelyDataVO.getRelyCaseId();
                    log.info("获取到的用例编号={}", caseId);
                    Integer executelogId = apiTestCaseService.executeInterfaceCase(new ExecuteInterfaceCaseParam(
                            caseId, "系统调度", null, chainNo, suiteId,
                            isFailedRetry, suitelogDetailNo, globalHeaders, globalParams, globalData, 4, casePreNo, false));

                    log.info("执行用例编号={}，执行日志编号={}", caseId, executelogId);
                    if (apiTestCaseExecuteLogService.findExecute(executelogId).getStatus() != 0) {
                        String nf = String.format("dependency related case [%s] execution failed or error, " +
                                "the execution log id was [%s]", caseId, executelogId);
                        log.error(nf);
                        throw new BusinessException(nf);
                    }
                    ApiTestCaseExecuteLogVO interfaceCaseExecutelogVO = apiTestCaseExecuteLogService.findExecute(executelogId);
                    String responseBody = interfaceCaseExecutelogVO.getResponseBody();

                    String responseHeaders = interfaceCaseExecutelogVO.getResponseHeaders();
                    log.info("依赖用例responseBody={}", responseBody);
                    log.info("依赖用例responseHeaders={}", responseHeaders);
                    Integer contentType =  interfaceCaseRelyDataVO.getContentType();
                    String expression = interfaceCaseRelyDataVO.getExtractExpression();
                    try {
                        if (contentType == 0) { // json
                            ArrayList jsonPathArray = JSONObject.parseObject(ParseUtil.parseJson(responseBody, expression), ArrayList.class);
                            if (jsonPathArray.isEmpty()) {
                                String nf = String.format("case 依赖项 [%s] json-path 提取内容为空, " +
                                        "the case id was [%s], the execution log id was [%s], the expression was [%s]", relyName, caseId, executelogId, expression);
                                log.error(nf);
                                throw new ParseException(nf);
                            }
                            try {
                                String value = jsonPathArray.get(index).toString();
                                redisUtil.stackPush(chainNo, chainNode(RelyType.INTERFACE_JSON, executelogId, relyExpress, value, TimeUtil.now() - start, expression));
                                s = s.replace(findStr, value);
                                log.info("jsonPath提取值并替换后的结果={}", s);
                            } catch (Exception e) {
                                String nf = String.format("json-path 索引超出范围, case 依赖项 [%s], index [%s]", relyName, index);
                                log.error(nf);
                                throw new ParseException(nf);
                            }
                        } else if (contentType == 1) { // html
                            ArrayList xpathArray = JSONObject.parseObject(ParseUtil.parseXml(responseBody, expression), ArrayList.class);
                            if (xpathArray.isEmpty()) {
                                String nf = String.format("case 依赖项 [%s] xpath 提取的内容为空, " +
                                        "案例编号是 [%s],执行日志 ID 为 [%s],表达式是……[%s]", relyName, caseId, executelogId, expression);
                                log.error(nf);
                                throw new ParseException(nf);
                            }
                            try {
                                String value = xpathArray.get(index).toString();
                                redisUtil.stackPush(chainNo, chainNode(RelyType.INTERFACE_HTML, executelogId, relyExpress, value, TimeUtil.now() - start, expression));
                                s = s.replace(findStr, value);
                                log.info("xpath提取值并替换后的结果={}", s);
                            } catch (Exception e) {
                                String nf = String.format("xpath index 超出越界,事件 依赖项 [%s], index [%s]", relyName, index);
                                log.error(nf);
                                throw new ParseException(nf);
                            }
                        } else if (contentType == 2) { // headers
                            JSONArray headerArray = (JSONArray) JSONObject.parseObject(responseHeaders, HashMap.class).get(expression);
                            if (null == headerArray || headerArray.isEmpty()) {
                                String nf = String.format("案例依赖项 [%s] 标头提取内容为空, " +
                                        "案例编号是 [%s], 执行日志ID为 [%s],这个表达式是 [%s]", relyName, caseId, executelogId, expression);
                                log.error(nf);
                                throw new ParseException(nf);
                            }
                            try {
                                String value = headerArray.get(index).toString();
                                redisUtil.stackPush(chainNo, chainNode(RelyType.INTERFACE_HEADER, executelogId, relyExpress, value, TimeUtil.now() - start, expression));
                                s = s.replace(findStr, value);
                                log.info("header提取值并替换后的结果={}", s);
                            } catch (Exception e) {
                                String nf = String.format("header 索引越界, 案例依赖项 [%s], index [%s]", relyName, index);
                                log.error(nf);
                                throw new ParseException(nf);
                            }
                        } else {
                            throw new BusinessException("不支持该提取类型");
                        }
                    } catch (BusinessException e) {
                        log.error("不支持该提取类型");
                        throw new BusinessException("不支持该提取类型");
                    } catch (Exception e) {
                        log.error("数组错误，附加项过多 , " + ExceptionUtil.msg(e));
                        throw new ParseException(e.getMessage());
                    }
                } catch (NumberFormatException e) {
                    log.error("数组索引参数不合法");
                    throw new ParseException("数组索引参数不合法");
                }
// 进入预置方法/动态SQL模式
            } else if (Pattern.matches(DEPENDENCY_REGEX_PARAMS, relyName)) {
                long start = TimeUtil.now();
                log.info("--------------------------------------进入预置方法/动态SQL模式");
                if (relyName.indexOf("(") != relyName.lastIndexOf("(") ||
                        relyName.indexOf(")") != relyName.lastIndexOf(")")) {
                    String nf = String.format("依赖项初始化方法或SQL [%s] 语法错误", relyName);
                    log.error(nf);
                    throw new ParseException(nf);
                }
                String methodName = relyName.substring(0, relyName.indexOf("("));
                log.info("预置方法名称/动态SQL依赖名称={}", methodName);
                String[] params = relyName.substring(relyName.indexOf("(") + 1, relyName.length() - 1)
                        .replaceAll(",\\s+", ",").split(",");
                RelyDataVO relyDataVO = relyDataService.findRelyDataByName(methodName);
                if (null == relyDataVO) {
                    String nf = String.format("init method or sql [%s] not found", relyName);
                    log.error(nf);
                    throw new ParseException(nf);
                }
                Integer type = relyDataVO.getType();
                if (type == 1) { //反射方法
                    log.info("--------------------------------------进入预置方法模式");

                    Method method;
                    String methodReturnValue;
                    Class<?> clazz;
                    Constructor<?> constructor;

                    try {
                        clazz = Class.forName("com.jmb.apiplatform.common.InvokeCenter");
                        constructor = clazz.getConstructor(byte.class);
                    } catch (ClassNotFoundException | NoSuchMethodException e) {
                        String nf = "com.jmb.apiplatform.common.InvokeCenter 构造函数未找到";
                        log.error(nf);
                        log.error(ExceptionUtil.msg(e));
                        throw new ParseException(nf);
                    }

                    if (params.length == 1 && "".equals(params[0])) {
                        params = new String[0];
                    }

                    try { // 尝试固定长度参数
                        clazz = Class.forName("com.jmb.apiplatform.common.InvokeCenter");
                        constructor = clazz.getConstructor(byte.class);
                        Class[] paramsList = new Class[params.length];
                        for (int i = 0; i < params.length; i++) {
                            paramsList[i] = String.class;
                            params[i] = params[i].substring(1, params[i].length() - 1);
                        }
                        log.info("固定长度参数，方法名称={}，方法参数={}", methodName, Arrays.toString(params));
                        method = clazz.getMethod(methodName, paramsList);
                        methodReturnValue = (String) method.invoke(constructor.newInstance(runEnv), params);
                        log.info("固定长度参数，预置方法执行并替换后的结果={}", s);
                    } catch (Exception e) {
                        log.error(e.getMessage());
                        // 尝试可变参数
                        try {
                            log.info("固定长度参数异常，尝试可变长度参数，方法名称={}，方法参数={}", methodName, Arrays.toString(params));
                            method = clazz.getMethod(methodName, String[].class);
                            methodReturnValue = (String) method.invoke(constructor.newInstance(runEnv), (Object) params);
                            log.info("固定长度参数异常，尝试可变长度参数，预置方法执行并替换后的结果={}", s);
                        } catch (Exception ee) {
                            String nf = String.format("依赖初始化方法执行错误，可能找不到或参数错误，方法为 [%s], 参数是[%s]", methodName, Arrays.toString(params));
                            log.error(nf);
                            log.error(ExceptionUtil.msg(ee));
                            throw new ParseException(nf);
                        }
                    }
                    redisUtil.stackPush(chainNo, chainNode(RelyType.INVOKE, relyDataVO.getId(), relyName, methodReturnValue, TimeUtil.now()-start, null));
                    s = s.replace(findStr, methodReturnValue);
                } else if (type >= 2 && type <= 6) { //sql 2sql-select 3sql-insert 4sql-update 5sql-delete 6sql-script
                    log.info("--------------------------------------进入动态SQL模式");
                    if (params.length == 1 && "".equals(params[0])) {
                        params = null;
                    } else {
                        for (int i = 0; i < params.length; i++) {
                            // 去除首尾引号
                            params[i] = params[i].substring(1, params[i].length() - 1);
                        }
                    }
                    Integer datasourceId = relyDataVO.getDatasourceId();
                    if (null == datasourceId) {
                        String nf = String.format("找不到数据源，数据源id为 [%s]", datasourceId);
                        log.error(nf);
                        throw new ParseException(nf);
                    }
                    DbVO dbVO = dbService.findDbById(datasourceId);
                    int status = dbVO.getStatus();
                    if (status == 1) {
                        String nf = String.format("找不到数据源，数据源id为 [%s]", datasourceId);
                        log.error(nf);
                        throw new ParseException(nf);
                    }
                    DbConnection datasource = env.datasource(dbVO, runEnv);
                    String url = datasource.getUrl();
                    String username = datasource.getUsername();
                    String password = datasource.getPassword();
                    // 支持动态sql
                    String sql = relyDataVO.getValue();
                    if (relyDataVO.getValue() != null) {
                        log.info("开始解析SQL，解析前SQL={}", sql);
                        sql = parseDependency(sql, chainNo, suiteId, isFailedRetry, suitelogDetailNo, globalHeaders, globalParams, globalData, casePreNo);
                        log.info("解析SQL完成，解析后SQL={}", sql);
                    }
                    log.info("SQL执行参数，SQL={}, params={}", sql, params);

                    String sqlResult;
                    if (type == 2) { // 查询
                        if (relyDataVO.getAnalysisRely().intValue() == 0) {
                            sqlResult = JdbcUtil.selectFirst(url, username, password, sql, params);
                        } else {
                            sqlResult = JdbcUtil.selectFirst(url, username, password, relyDataVO.getValue(), params);
                        }
                        redisUtil.stackPush(chainNo, chainNode(RelyType.SQL_SELECT, relyDataVO.getId(), relyName, sqlResult, TimeUtil.now()-start, null));
                    } else if (type == 3) { // 新增
                        if (relyDataVO.getEnableReturn().intValue() == 0) {
                            sqlResult = String.valueOf(JdbcUtil.insert(url, username, password, sql, params));
                        } else {
                            JdbcUtil.insert(url, username, password, sql, params);
                            sqlResult = "";
                        }
                        redisUtil.stackPush(chainNo, chainNode(RelyType.SQL_INSERT, relyDataVO.getId(), relyName, sqlResult, TimeUtil.now()-start, null));
                    } else if (type == 4) { // 修改
                        if (relyDataVO.getAnalysisRely().intValue() == 0) {
                            sqlResult = JdbcUtil.update(url, username, password, sql, params);
                        } else {
                            sqlResult = JdbcUtil.update(url, username, password, relyDataVO.getValue(), params);
                        }
                        redisUtil.stackPush(chainNo, chainNode(RelyType.SQL_UPDATE, relyDataVO.getId(), relyName, sqlResult, TimeUtil.now()-start, null));
                    } else if (type == 5) { // 删除
                        if (relyDataVO.getAnalysisRely().intValue() == 0) {
                            sqlResult = JdbcUtil.delete(url, username, password, sql, params);
                        } else {
                            sqlResult = JdbcUtil.delete(url, username, password, relyDataVO.getValue(), params);
                        }
                        redisUtil.stackPush(chainNo, chainNode(RelyType.SQL_DELETE, relyDataVO.getId(), relyName, sqlResult, TimeUtil.now()-start, null));
                    } else { // 脚本
                        if (relyDataVO.getAnalysisRely().intValue() == 0) {
                            sqlResult = JdbcUtil.script(sql, url, username, password, true);
                        } else {
                            sqlResult = JdbcUtil.script(relyDataVO.getValue(), url, username, password, true);
                        }
                        redisUtil.stackPush(chainNo, chainNode(RelyType.SQL_SCRIPT, relyDataVO.getId(), relyName, sqlResult, TimeUtil.now()-start, null));
                    }
                    s = s.replace(findStr, sqlResult);
                }

// 进入普通依赖数据模式
            } else {
                long start = TimeUtil.now();
                log.info("--------------------------------------进入普通依赖数据模式");
                ApiTestCaseRelyDataTO caseRelyDataTO = new ApiTestCaseRelyDataTO();
                caseRelyDataTO.setRelyName(relyName);
                ApiTestCaseRelyDataVO relyData = apiTestCaseRelyDataService.findIfRelyDataByName(relyName);
                if (null == relyData) {
                    RelyDataVO relyDataVO = relyDataService.findRelyDataByName(relyName);
                    if (null == relyDataVO) {
                        String nf = String.format("依赖项 [%s] 未找到", relyName);
                        log.error(nf);
                        throw new ParseException(nf);
                    } else {
                        Integer type = relyDataVO.getType();
                        if (type == 0) {
                            String value = relyDataVO.getValue();
                            s = s.replace(findStr, value);
                            redisUtil.stackPush(chainNo, chainNode(RelyType.CONST, type, relyName, value, TimeUtil.now() - start, null));
                        } else if (type >= 2 && type <= 6) {
                            Integer datasourceId = relyDataVO.getDatasourceId();
                            if (null == datasourceId) {
                                String nf = String.format("找不到数据源，数据源id是[%s]", datasourceId);
                                log.error(nf);
                                throw new ParseException(nf);
                            }
                            DbVO dbVO = dbService.findDbById(datasourceId);
                            int status = dbVO.getStatus();
                            if (status == 1) {
                                String nf = String.format("找不到数据源，数据源id是 [%s]", datasourceId);
                                log.error(nf);
                                throw new ParseException(nf);
                            }
                            DbConnection datasource = env.datasource(dbVO, runEnv);
                            String url = datasource.getUrl();
                            String username = datasource.getUsername();
                            String password = datasource.getPassword();
                            String sql = relyDataVO.getValue();
                            if (relyDataVO.getValue() != null) {
                                log.info("开始解析SQL，解析前SQL={}", sql);
                                sql = parseDependency(sql, chainNo, suiteId, isFailedRetry, suitelogDetailNo, globalHeaders, globalParams, globalData, casePreNo);
                                log.info("解析SQL完成，解析后SQL={}", sql);
                            }
                            log.info("SQL执行参数，SQL={}", sql);
                            String sqlResult;
                            if (type == 2) { // 查询
                                if (relyDataVO.getAnalysisRely().intValue() == 0) {
                                    sqlResult = JdbcUtil.selectFirst(url, username, password, sql, null);
                                } else {
                                    sqlResult = JdbcUtil.selectFirst(url, username, password, relyDataVO.getValue(), null);
                                }
                                log.info("未处理的方法");
                                redisUtil.stackPush(chainNo, chainNode(RelyType.SQL_SELECT, relyDataVO.getId(), relyName, sqlResult, TimeUtil.now()-start, null));
                            } else if (type == 3) { // 新增
                                if (relyDataVO.getEnableReturn().intValue() == 0) {
                                    sqlResult = String.valueOf(JdbcUtil.insert(url, username, password, sql, null));
                                } else {
                                    JdbcUtil.insert(url, username, password, sql, null);
                                    sqlResult = "";
                                }
                                redisUtil.stackPush(chainNo, chainNode(RelyType.SQL_INSERT, relyDataVO.getId(), relyName, sqlResult, TimeUtil.now()-start, null));
                            } else if (type == 4) { // 修改
                                if (relyDataVO.getAnalysisRely().intValue() == 0) {
                                    sqlResult = JdbcUtil.update(url, username, password, sql, null);
                                } else {
                                    sqlResult = JdbcUtil.update(url, username, password, relyDataVO.getValue(), null);
                                }
                                redisUtil.stackPush(chainNo, chainNode(RelyType.SQL_UPDATE, relyDataVO.getId(), relyName, sqlResult, TimeUtil.now()-start, null));
                            } else if (type == 5) { // 删除
                                if (relyDataVO.getAnalysisRely().intValue() == 0) {
                                    sqlResult = JdbcUtil.delete(url, username, password, sql, null);
                                } else {
                                    sqlResult = JdbcUtil.delete(url, username, password, relyDataVO.getValue(), null);
                                }
                                redisUtil.stackPush(chainNo, chainNode(RelyType.SQL_DELETE, relyDataVO.getId(), relyName, sqlResult, TimeUtil.now()-start, null));
                            } else { // 脚本
                                if (relyDataVO.getAnalysisRely().intValue() == 0) {
                                    sqlResult = JdbcUtil.script(sql, url, username, password, true);
                                } else {
                                    sqlResult = JdbcUtil.script(relyDataVO.getValue(), url, username, password, true);
                                }
                                redisUtil.stackPush(chainNo, chainNode(RelyType.SQL_SCRIPT, relyDataVO.getId(), relyName, sqlResult, TimeUtil.now()-start, null));
                            }
                            s = s.replace(findStr, sqlResult);
                            } else if (type == 6) {
                                String nf = String.format("依赖项 [%s]未找到，请确保它不是方法", relyName);
                                log.error(nf);
                                throw new ParseException(nf);
                            }
                        }
                    }
                else {
                        Integer caseId = relyData.getRelyCaseId();
                    Integer executelogId = apiTestCaseService.executeInterfaceCase(new ExecuteInterfaceCaseParam(caseId,
                            "系统调度", null, chainNo, suiteId, isFailedRetry, suitelogDetailNo,
                            globalHeaders, globalParams, globalData, 4, casePreNo, false));
                    if (apiTestCaseExecuteLogService.findExecute(executelogId).getStatus() != 0) {
                        String nf = String.format("依赖相关案例 [%s] 执行失败或错误, " +
                                "the execution log id was [%s]", caseId, executelogId);
                        log.error(nf);
                        throw new BusinessException(nf);
                    }
                    ApiTestCaseExecuteLogVO apiTestCaseExecuteLogVO = apiTestCaseExecuteLogService.findExecute(executelogId);
                    String responseBody = apiTestCaseExecuteLogVO.getResponseBody();
                    String responseHeaders = apiTestCaseExecuteLogVO.getResponseHeaders();
                        log.info("依赖用例responseBody={}", responseBody);
                        log.info("依赖用例responseHeaders={}", responseHeaders);
                        Integer contentType =  caseRelyDataTO.getContentType();
                        String expression = relyData.getExtractExpression();
                        try {
                            if (contentType == 0) { // json
                                ArrayList jsonPathArray = JSONObject.parseObject(ParseUtil.parseJson(responseBody, expression), ArrayList.class);
                                if (jsonPathArray.isEmpty()) {
                                    String nf = String.format("案例依赖项 [%s] json-path 提取的内容为空, " +
                                            "案例编号是 [%s], 执行日志ID为 [%s], 表达式是 [%s]", relyName, caseId, executelogId, expression);
                                    log.error(nf);
                                    throw new ParseException(nf);
                                }
                                if (jsonPathArray.size() == 1) {
                                    Object o = jsonPathArray.get(0);
                                    redisUtil.stackPush(chainNo, chainNode(RelyType.INTERFACE_JSON, executelogId, relyName, o.toString(), TimeUtil.now() - start, expression));
                                    s = s.replace(findStr, o.toString());
                                } else {
                                    redisUtil.stackPush(chainNo, chainNode(RelyType.INTERFACE_JSON, executelogId, relyName, JSON.toJSONString(jsonPathArray), TimeUtil.now() - start, expression));
                                    s = s.replace(findStr, JSON.toJSONString(jsonPathArray));
                                }
                                log.info("jsonPath提取值并替换后的结果={}", s);
                            } else if (contentType == 1) { // html
                                ArrayList xpathArray = JSONObject.parseObject(ParseUtil.parseXml(responseBody, expression), ArrayList.class);
                                if (xpathArray.isEmpty()) {
                                    String nf = String.format("案例依赖项 [%s] xpath 提取的内容为空, " +
                                            "案例编号是 [%s], 执行日志ID为 [%s], 表达式是 [%s]", relyName, caseId, executelogId, expression);
                                    log.error(nf);
                                    throw new ParseException(nf);
                                }
                                if (xpathArray.size() == 1) {
                                    Object o = xpathArray.get(0);
                                    redisUtil.stackPush(chainNo, chainNode(RelyType.INTERFACE_HTML, executelogId, relyName, o.toString(), TimeUtil.now() - start, expression));
                                    s = s.replace(findStr, o.toString());
                                } else {
                                    redisUtil.stackPush(chainNo, chainNode(RelyType.INTERFACE_HTML, executelogId, relyName, JSON.toJSONString(xpathArray), TimeUtil.now() - start, expression));
                                    s = s.replace(findStr, JSON.toJSONString(xpathArray));
                                }
                                log.info("xml提取值并替换后的结果={}", s);
                            } else if (contentType == 2) { // headers
                                ArrayList headerArray = JSONObject.parseObject(ParseUtil.parseJson(responseHeaders, expression), ArrayList.class);
                                if (headerArray == null || headerArray.isEmpty()) {
                                    String nf = String.format("案例依赖项 [%s] header's json-path 提取的内容为空,, " +
                                            "案例编号是s [%s],执行日志ID为 [%s], 表达式是 [%s]", relyName, caseId, executelogId, expression);
                                    log.error(nf);
                                    throw new ParseException(nf);
                                } else {
                                    if (headerArray.size() == 1) {
                                        Object o = headerArray.get(0);
                                        redisUtil.stackPush(chainNo, chainNode(RelyType.INTERFACE_HEADER, executelogId, relyName, o.toString(), TimeUtil.now() - start, expression));
                                        s = s.replace(findStr, o.toString());
                                    } else {
                                        redisUtil.stackPush(chainNo, chainNode(RelyType.INTERFACE_HEADER, executelogId, relyName, JSON.toJSONString(headerArray), TimeUtil.now() - start, expression));
                                        s = s.replace(findStr, JSON.toJSONString(headerArray));
                                    }
                                    log.info("header提取值并替换后的结果={}", s);
                                }
                            } else {
                                throw new BusinessException("不支持提取类型。");
                            }
                        } catch (BusinessException e) {
                            String nf = "不支持提取类型。";
                            log.error(nf);
                            throw new BusinessException(nf);
                        } catch (Exception e) {
                            String nf = String.format("解析依赖错误, errorMsg [%s]", ExceptionUtil.msg(e));
                            log.error(nf);
                            throw new ParseException(nf);
                        }
                    }
                }
            }
        return s;
    }


    /**
     *
     * @param text 字符串文本数据
     * @param suitelogDetailNo 非空使用测试套件，否则使用临时
     * @param casePreNo 组合使用前置用例缓存 redis hash key
     * @param chainNo  链路跟踪redis key
     * @return 解析后的字符串
     * @throws Exception 异常
     */
    public String parseProcessor(String text,String suitelogDetailNo,String casePreNo, String chainNo) throws ParseException {
        log.info("--------------------------------------开始处理器提取解析流程--------------------------------------");
        log.info("--------------------------------------待解析字符串原文={}", text);
        // 为空把空数据返回，不做处理
        if (text != null && !text.isEmpty()) {
            return text;
        }
        Pattern compile = Pattern.compile(PROCESSOR_REGEX);
        Matcher matcher = compile.matcher(text);
        while (matcher.find()) {
            long start = TimeUtil.now();
            String finds = matcher.group();
            // 获取到处理器名称
            String  processorName= finds.substring(2, finds.length() - 1);
            Object redisResult;
            if (casePreNo == null){
                if (suitelogDetailNo == null){
                    log.info("--------------------------------------测试套件未找到处理器{},使用临时变量域", processorName);
                    redisResult= redisUtil.hashGet(NoUtil.TEMP_POST_PROCESSOR_NO,processorName);
                }else {
                    log.info("--------------------------------------使用测试套件{}使用处理器{}", suitelogDetailNo, processorName);
                    redisResult = redisUtil.hashGet(suitelogDetailNo,processorName);
                }
            }else {
                log.info("--------------------------------------使用前置用例{}使用处理器{}", casePreNo, processorName);
                redisResult = redisUtil.hashGet(casePreNo,processorName);
            }
            if (redisResult == null){
                String cw = "未找到处理器"+processorName;
                log.error(cw);
                throw new ParseException(cw);
            }
            String redisResultString = redisResult.toString();
            redisUtil.stackPush(chainNo,chainNode(RelyType.READ_PROCESSOR,null,processorName,redisResultString, TimeUtil.now()-start,null));
            text = text.replace(finds,redisResultString);
            // 写入处理器日志，抛弃了。
        }
        return text;
    }



    /**
     * 提取依赖名称
     * @param text 字符串文本数据
     * @return 依赖名称的列表
     */
    public ArrayList<String> extractDependencyName(String text) {
        ArrayList<String> list = new ArrayList<>();
        if (text!=null){
            // 去除处理器，否则依赖包含处理器解析报错,解析不支持
            text = text.replaceAll(PROCESSOR_REGEX, "");
            Matcher matcher = Pattern.compile(DEPENDENCY_REGEX).matcher(text);
            while (matcher.find()){
                String finds = matcher.group();
                String dependencyExpression = finds.substring(2, finds.length() - 1);
                if (Pattern.matches(DEPENDENCY_REGEX_INDEX, dependencyExpression)) { // 数组下标 带[]
                    String dependencyNam = dependencyExpression.substring(0, dependencyExpression.indexOf("["));
                    list.add(dependencyNam);
                }else if (Pattern.matches(DEPENDENCY_REGEX_PARAMS, dependencyExpression)){ // 方法或者sql带()
                    String dependencyNam = dependencyExpression.substring(0, dependencyExpression.indexOf("("));
                    list.add(dependencyNam);
                }else { // 普通模式
                    list.add(dependencyExpression);
                }

            }
        }
        return list;
    }
}
